using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;

using System.IO;
using System.Collections;
using System.Reflection;
using CreamX.Framework.Game;
using CreamXStudio.Classes;
using CreamX.Framework.Game.Components;

using Microsoft.Xna.Framework.Graphics;
using CreamXStudio.Screens;
using CreamX.Framework;
using CreamX.Framework.Game.GUI;

using CreamX.Framework.Game.GameEngine;
using Microsoft.Xna.Framework;
using CreamX.Framework.Attributes;

namespace CreamXStudio
{
    public partial class Form1 : Form
    {
        #region Variables
        const string MATERIALNODE = "MATERIALS";
        const string OBJECTNODE = "OBJECTS";
        const string GUINODE= "GUI";
        const string TEMPLATESNODE = "TEMPLATES";

        public static Form1 Instance;
        #endregion
        #region Constructor
        public Form1()
        {   
            InitializeComponent();
            Instance = this;
            this.Load += new EventHandler(FormLoad);
            UpdateToolBars();
        }
        #endregion
        #region Methods
        void FormLoad(object sender, EventArgs e)
        {
            InitCreamXGameEngine();
            levelRenderer.DoubleClick += new EventHandler(LevelRendererDoubleClick);
            levelRenderer.MouseUp += new MouseEventHandler(LevelRendererMouseUp);
        }

        void LevelRendererMouseUp(object sender, MouseEventArgs e)
        {
            try
            {
                bool found = false;
                if (levelRenderer.SelectedObject != null)
                {
                    foreach (TreeNode node in treeSceneData.Nodes[OBJECTNODE].Nodes)
                    {
                        if (node.Tag == levelRenderer.SelectedObject)
                        {
                            treeSceneData.SelectedNode = node;
                            found = true;
                            break;
                        }
                    }
                    trackRotation.Value = (int)MathHelper.ToDegrees(levelRenderer.SelectedObject.Rotation);
                    trackObjectScale.Value = (int)levelRenderer.SelectedObject.Scale*10;
                    UpdateCustomEditor();
                }
                if (!found)
                {
                    foreach (TreeNode node in treeSceneData.Nodes[TEMPLATESNODE].Nodes)
                    {
                        if (node.Tag == levelRenderer.SelectedObject)
                        {
                            treeSceneData.SelectedNode = node;
                            found = true;
                            break;
                        }
                    }
                }
            }
            catch 
            {
                //WHY? // Cant Remember :S
            }
   
        }

        private void UpdateCustomEditor()
        {
            objectEditor1.SelectedObject = levelRenderer.SelectedObject;
            cboComponents.Items.Clear();
            cboComponents.DisplayMember = "name";
            cboComponents.ValueMember = "componentInstance";
            foreach (CreamXComponent comp in levelRenderer.SelectedObject.Components)
            {
                CreamXComponentAttribute att = GetComponentAttribute(comp);
                
                ComponentHolder comp1=new ComponentHolder();
                comp1.name=att.FriendlyName;
                comp1.componentInstance=comp;
                cboComponents.Items.Add(comp);
            }
        }

        private CreamXComponentAttribute GetComponentAttribute(CreamXComponent comp)
        {
            object[] objc = comp.GetType().GetCustomAttributes(true);
            foreach (object var in objc)
            {
                if (var is CreamXComponentAttribute)
                    return var as CreamXComponentAttribute;
            }
            return null;
        }


        void LevelRendererDoubleClick(object sender, EventArgs e)
        {
            if (levelRenderer.SelectedObject != null)
            {
                EditSelectedTreeNode();
            }
        }

        private void OpenProjectFile()
        {
            try
            {
                ToolStripDropDownItem item = mnuComponents;
                OpenFileDialog __ofd = new OpenFileDialog();
                __ofd.Filter = "CreamX Project File|*.cxproj";
                __ofd.ShowDialog();
                if (__ofd.FileName != string.Empty)
                {
                    SessionData.Instance.OpenProject(__ofd.FileName);
                    foreach (KeyValuePair<string,Type> t in SessionData.Instance.ProjectSettings.CreamXComponents)
                    {
                        ToolStripItem componentMenuItem = item.DropDownItems.Add(t.Key);
                        componentMenuItem.Tag = t.Value;
                        componentMenuItem.Click += new EventHandler(componentMenuItem_Click);
                    }
                    lblProjectFile.Text = __ofd.FileName;
                    AddScenes();
                }

                //InitCreamXGameEngine();
            }
            catch (Exception err)
            {
                CreamXErrorHandler.LogError(err);
            }
            finally
            {
                UpdateToolBars();
            }
        }

        private void AddScenes()
        {
            menubtnOpenScenes.DropDownItems.Clear();
            string[] files=Directory.GetFiles(SessionData.Instance.ProjectSettings.GetScenePath());
            foreach (string file in files)
            {
                if (file.EndsWith(".cxscene"))
                {
                    ToolStripItem item = menubtnOpenScenes.DropDownItems.Add(Path.GetFileNameWithoutExtension(file));
                    item.Click += new EventHandler(menuSceneFileClick);
                    item.Tag = file;
                }
            }

        }

        protected override void OnClosing(CancelEventArgs e)
        {
            DialogResult res = MessageBox.Show("Would you like to save?","Save",MessageBoxButtons.YesNo);
            if (res == DialogResult.Yes)
            {
                SessionData.Instance.SaveProject();
            }
            base.OnClosing(e);
        }
        
        void menuSceneFileClick(object sender, EventArgs e)
        {
            string fname = ((ToolStripItem)sender).Tag.ToString();
            SessionData.Instance.OpenScene(fname);
            RefreshScreenData();
            lblProjectFile.Text = fname;
            mainSplitContainer.Enabled = true;
            UpdateToolBars();
        }

        void componentMenuItem_Click(object sender, EventArgs e)
        {
            if (treeSceneData.SelectedNode.Tag != null)
            {
                CreamXObject _o = (CreamXObject)treeSceneData.SelectedNode.Tag;
                Assembly _assembly = SessionData.Instance.ProjectSettings.AttributeInfo[((ToolStripItem)sender).Text];
                Type at=((ToolStripItem)sender).Tag as Type;
                object oo=_assembly.CreateInstance(at.FullName);
                if (oo != null)
                {
                    _o.Components.Add(oo as CreamXComponent);
                    /*foreach (PropertyInfo prop in at.GetProperties())
                    {
                        object[] attribs = prop.GetCustomAttributes(true);
                        if (attribs.Length > 0)
                        {
                            foreach (object obj in attribs)
                            {
                                if (obj is CreamXComponentPropertyAttribute)
                                {

                                }
                            }
                        }
                    }*/
                }
                PopulateObjects();
                PopulateTemplates();
            }
        
        }
        
        private void InitCreamXGameEngine()
        {
            CreamXGameEngine.Instance = new CreamXGameEngine(this.levelRenderer.GraphicsDevice);
            SessionData.Instance.contentManager = new Microsoft.Xna.Framework.Content.ContentManager(levelRenderer.Services);
            string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
            SessionData.Instance.LoadAssemblies(path);
        }

        private void DoOpenSceneFile()
        {
            try
            {
                OpenFileDialog __ofd = new OpenFileDialog();
                __ofd.Filter = "CreamX Scene File|*.cxscene";
                __ofd.InitialDirectory = SessionData.Instance.ProjectSettings.GetScenePath();
                __ofd.ShowDialog();
                if (__ofd.FileName != string.Empty)
                {
                    SessionData.Instance.OpenScene(__ofd.FileName);

                    RefreshScreenData();
                    lblScene.Text = __ofd.FileName;
                }
            }
            catch (Exception err)
            {
                CreamXErrorHandler.LogError(err);
            }
            finally
            {
                UpdateToolBars();
            }
        }

        private void RefreshScreenData()
        {

            LoadContent();
            trackCamZoom.Value = (int)(SessionData.Instance.CurrentScene.SceneData.Camera.Zoom * 10);
            treeSceneData.Nodes.Clear();
            AddDefaultRootNodes();
            PopulateMaterials();
            PopulateObjects();
            PopulateTemplates();
            PopulateGui();
            PopulateOther();
            levelRenderer._data = SessionData.Instance.CurrentScene.SceneData;
        }

        private void PopulateOther()
        {
            //Camera
            numCamPosX.Value = (decimal)SessionData.Instance.CurrentScene.SceneData.Camera.Position.X;
            numCamPosY.Value = (decimal)SessionData.Instance.CurrentScene.SceneData.Camera.Position.Y;

            numSizeX.Value = (decimal)SessionData.Instance.CurrentScene.SceneData.Camera.Size.X;
            numSizeY.Value = (decimal)SessionData.Instance.CurrentScene.SceneData.Camera.Size.Y;

            trackZoom.Value = (int)(SessionData.Instance.CurrentScene.SceneData.Camera.Zoom * 10f);
        }

        private void PopulateObjects()
        {
            treeSceneData.Nodes[OBJECTNODE].Nodes.Clear();
            foreach (CreamXObject __o in SessionData.Instance.CurrentScene.SceneData.Objects)
            {
                TreeNode __node = treeSceneData.Nodes[OBJECTNODE].Nodes.Add(__o.Name);
                __node.ImageIndex = __node.SelectedImageIndex = __node.Parent.ImageIndex;
                if (__o is CreamXTileGrid)
                {
                    
                    __o.GetComponent<CreamXTileGridComponent>().LoadData(true);
                }
                __node.Tag = __o;
                AddComponents(__node, __o);
            }
        }

        private void AddComponents(TreeNode __node, CreamXObject __o)
        {
            if(__o.Components.Count>0)
            {
                TreeNode __comps=__node.Nodes.Add("COMPONENTS","Components");
                foreach (CreamXComponent __comp in __o.Components)
                {
                    TreeNode __nd = __comps.Nodes.Add(__comp.GetType().Name);
                    __nd.Tag = __comp;
                }
            }
        }

        private void PopulateTemplates()
        {
            treeSceneData.Nodes[TEMPLATESNODE].Nodes.Clear();
            foreach (CreamXObject __o in SessionData.Instance.CurrentScene.SceneData.TemplateObjects)
            {
                TreeNode __node = treeSceneData.Nodes[TEMPLATESNODE].Nodes.Add(__o.Name);
                __node.ImageIndex = __node.SelectedImageIndex = __node.Parent.ImageIndex;
                __node.Tag = __o;
                AddComponents(__node, __o);
            }
        }

        private void LoadContent()
        {
            SessionData.Instance.contentManager = new Microsoft.Xna.Framework.Content.ContentManager(levelRenderer.Services, SessionData.Instance.ContentBuilder.OutputDirectory);
            SessionData.Instance.ContentBuilder.Clear();
            foreach (CreamXMaterialBase __mat in SessionData.Instance.CurrentScene.SceneData.Materials)
            {
                string __fname = SessionData.Instance.GetFullMaterialPath(__mat.MaterialFilename);
                if (!File.Exists(__fname))
                    MessageBox.Show("File Not Found " + __fname);
                
                SessionData.Instance.ContentBuilder.Add(__fname, 
                    __mat.Name, null, "TextureProcessor");
                AddImageToCache(__mat.Name, SessionData.Instance.GetFullMaterialPath(__mat.MaterialFilename));
            }


            string __success=SessionData.Instance.ContentBuilder.Build();

            if (__success != null)
            {
                CreamXErrorHandler.LogError(new Exception(__success));
            }
            else
            {
                foreach (CreamXMaterialBase var in SessionData.Instance.CurrentScene.SceneData.Materials)
                {
                    //var.MaterialFilename.Substring(0,var.MaterialFilename.LastIndexOf("."))
                    var.Texture = SessionData.Instance.contentManager.Load<Texture2D>( var.Name);
                }
                levelRenderer._builder = SessionData.Instance.ContentBuilder;
            }
        }

        private void PopulateMaterials()
        {
            treeSceneData.Nodes[MATERIALNODE].Nodes.Clear();
            foreach (CreamXMaterialBase __mat in SessionData.Instance.CurrentScene.SceneData.Materials)
            {
                TreeNode __nd = treeSceneData.Nodes[MATERIALNODE].Nodes.Add(__mat.Name);
                __nd.Tag = __mat;
            }
        }

        private void AddDefaultRootNodes()
        {
            TreeNode __nd= treeSceneData.Nodes.Add(MATERIALNODE, "Scene Materials",0);
            __nd.SelectedImageIndex = 0;
            
            __nd=treeSceneData.Nodes.Add(OBJECTNODE, "Scene Objects",1);
            __nd.SelectedImageIndex = 1;
            
            __nd=treeSceneData.Nodes.Add(TEMPLATESNODE, "Scene Templates",2);
            __nd.SelectedImageIndex = 2;

            __nd = treeSceneData.Nodes.Add(GUINODE, "GUI", 2);
            __nd.SelectedImageIndex = 2;
            
        }

        private void sceneToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DoOpenSceneFile();
        }

        private void projectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenProjectFile();
        }
        
        private void numericUpDown2_ValueChanged(object sender, EventArgs e)
        {
            Vector2 pos = new Vector2((float)numCamPosX.Value,
                                    (float)numCamPosY.Value);
            SessionData.Instance.CurrentScene.SceneData.Camera.SetPosition(pos);
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (treeSceneData.SelectedNode.Tag != null)
            {
                if (treeSceneData.SelectedNode.Tag is CreamXObject)
                {
                    levelRenderer.SelectedObject =(CreamXObject)treeSceneData.SelectedNode.Tag;
                    levelRenderer.SetSelectedBox(levelRenderer.SelectedObject);
                    levelRenderer.Invalidate();
                }
            }
        }

        private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            EditSelectedTreeNode();
        }

        private void EditSelectedTreeNode()
        {
            if (treeSceneData.SelectedNode == null)
                return;
            if (treeSceneData.SelectedNode.Tag != null)
            {
                object __data = treeSceneData.SelectedNode.Tag;
                EditorControlBase __base = null;
                if (__data is CreamXMaterialBase)
                {
                    __base = new MaterialEditor();
                }
                else if (__data is CreamXTileGrid)
                {
                    __base = new TileEditor();
                }
                else if (__data is CreamXParticleEffect)
                {
                    __base = new ParticleEditor();
                }
                else if (__data is CreamXObject)
                {
                    __base = new ObjectEditorHolder();
                }
                if(__base!=null)
                    EditorDialog.LoadEditor(__base, __data);
                
            }
        }

        private void mnuAddMaterial_Click(object sender, EventArgs e)
        {
            AddNewMaterial();
        }

        private void AddNewMaterial()
        {
            OpenFileDialog __ofd = new OpenFileDialog();
            __ofd.ShowDialog();
            if (__ofd.FileName != string.Empty)
            {
                CreamXMaterialBase _base = new CreamXMaterialBase();
                _base.Name = SessionData.Instance.GetNewMaterialName(__ofd.FileName); ;
                //Add to image cache
                AddImageToCache(_base.Name, __ofd.FileName);
                _base.MaterialFilename = __ofd.FileName.Replace(SessionData.Instance.ProjectSettings.RootDIR, "");
                string __fname = SessionData.Instance.GetFullMaterialPath(_base.MaterialFilename);
                SessionData.Instance.ContentBuilder.Add(__fname,
                    _base.Name, null, "TextureProcessor");
                SessionData.Instance.ContentBuilder.Build();
                _base.Texture = SessionData.Instance.contentManager.Load<Texture2D>(_base.Name);
                SessionData.Instance.CurrentScene.SceneData.Materials.Add(_base);


                PopulateMaterials();
            }
        }

        private void AddImageToCache(string name,string filename)
        {
            if (System.IO.File.Exists(filename))
            {
                if (SessionData.Instance.ImageCache.ContainsKey(name))
                    SessionData.Instance.ImageCache.Remove(name);
                SessionData.Instance.ImageCache.Add(name,
                        Image.FromFile(filename));
            }
            else
            {
                throw new System.IO.FileNotFoundException(filename);
            }
        }

        private void templateObjectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddNewTemplate();
        }

        private void AddNewTemplate()
        {
            CreamXObject _o = new CreamXObject();
            _o.Template = true;
            _o.Scale = 1;
            _o.Name = "[TEMPLATE]";
            _o.SetPosition( Vector2.Zero);
            _o.Visible = true;
            SessionData.Instance.CurrentScene.SceneData.TemplateObjects.Add(_o);
            PopulateTemplates();
            treeSceneData.Nodes[TEMPLATESNODE].Expand();
            treeSceneData.SelectedNode = treeSceneData.Nodes[TEMPLATESNODE];
        }

        private void AddNewSceneObject()
        {
            CreamXObject _o = new CreamXObject();
            _o.Template = false;
            _o.Scale = 1;
            _o.SetPosition(Vector2.Zero);
            _o.Visible = true;
            _o.Name = "[NULL]";
            SessionData.Instance.CurrentScene.SceneData.Objects.Add(_o);
            PopulateObjects();
            treeSceneData.Nodes[OBJECTNODE].Expand();
            treeSceneData.SelectedNode = treeSceneData.Nodes[OBJECTNODE];
        }

        private void saveToolStripButton_Click(object sender, EventArgs e)
        {
            SessionData.Instance.SaveProject();
        }

        private void tileGridToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CreamXTileGrid tilegrid = new CreamXTileGrid();
            tilegrid.Scale = 1;
            tilegrid.Name = "TileGrid1";
            SessionData.Instance.CurrentScene.SceneData.Objects.Add(tilegrid);
            PopulateObjects();
        }

        internal void RefreshAll()
        {
            PopulateMaterials();
            PopulateObjects();
            PopulateGui();
            PopulateTemplates();
            PopulateOther();
        }

        private void PopulateGui()
        {
            //treeView1.Nodes[GUINODE].Nodes.Clear();
            //foreach (GUIButton __o in SessionData.Instance.CurrentScene.SceneData.GUI.Buttons)
            //{
            //    TreeNode __node = treeView1.Nodes[GUINODE].Nodes.Add(__o.Name);
            //    __node.ImageIndex = __node.SelectedImageIndex = __node.Parent.ImageIndex;
            //    __node.Tag = __o;
            //}

        }

        private void treeView1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                TreeNode nd = treeSceneData.GetNodeAt(e.X, e.Y);
                if (nd != null)
                    treeSceneData.SelectedNode = nd;
            }
        }

        private void menuRemove_Click(object sender, EventArgs e)
        {
            RemoveSelectedObject();
        }

        private void RemoveSelectedObject()
        {
            object o = treeSceneData.SelectedNode.Tag;
            if (o != null)
            {
                if (o is CreamXObject)
                {
                    if (((CreamXObject)o).Template)
                        SessionData.Instance.CurrentScene.SceneData.TemplateObjects.Remove((CreamXObject)o);
                    else
                        SessionData.Instance.CurrentScene.SceneData.Objects.Remove((CreamXObject)o);
                }
                if (o is CreamXComponent)
                {
                    CreamXObject obj = (CreamXObject)treeSceneData.SelectedNode.Parent.Parent.Tag;
                    obj.Components.Remove((CreamXComponent)o);
                }
                RefreshAll();
            }
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            if(SessionData.Instance.CurrentScene!=null)
                SessionData.Instance.CurrentScene.SceneData.Camera.Zoom = (float)(trackCamZoom.Value / 10f);
        }

        private void UpdateToolBars()
        {
            mnuOpenScene.Enabled = mnubtnOpenScene.Enabled = SessionData.Instance.ProjectFileOpen;
            
            menubtnOpenScenes.Visible=SessionData.Instance.ProjectSettings!=null;
            
            if (SessionData.Instance.CurrentScene != null)
            {
                menubtnSave.Enabled = true;
                menubtnAddStuff.Enabled = true;
                menubtnClone.Enabled = true;
                menubtnDelete.Enabled = true;
                
            }
            else
            {
                menubtnSave.Enabled = false;
                
                menubtnAddStuff.Enabled = false;
                menubtnClone.Enabled = false;
                menubtnDelete.Enabled = false;
            }
        }

        private void sceneObjectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddNewSceneObject();
        }

        private void createCloneAsSceneItemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (treeSceneData.SelectedNode.Tag == null) return;
            try
            {
                CreamXObject o = (CreamXObject)treeSceneData.SelectedNode.Tag;
                CreamXObject __o;
                if (o.Template)
                    __o = SessionData.Instance.CurrentScene.SceneData.CreateClone(o.Name);
                else
                    __o = (CreamXObject)SessionData.Instance.CurrentScene.SceneData.GetObject(o.Name).Clone();

                if (__o != null)
                {
                    __o.SetPosition(0, 0);
                    SessionData.Instance.CurrentScene.SceneData.Objects.Add(__o);
                }
                RefreshAll();
            }
            catch
            {
            }
        }

        private void gUIButtonToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddNewGUIButton();
        }

        private void AddNewGUIButton()
        {
            GUIButton _o = new GUIButton();
            _o.Template = false;
            _o.Scale = 1;
            _o.SetPosition(Vector2.Zero);
            _o.Visible = true;
            _o.Name = "btnNull";
            //SessionData.Instance.CurrentScene.SceneData.GUI.Buttons.Add(_o);
            RefreshAll();

        }

        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            if (levelRenderer.SelectedObject != null)
            {
                float rot =MathHelper.ToRadians(trackRotation.Value);
                levelRenderer.SelectedObject.Rotation = rot;
            }
        }

        private void trackBar3_Scroll(object sender, EventArgs e)
        {
            if (levelRenderer.SelectedObject != null)
            {
                float rot = (float)trackObjectScale.Value/10f;
                levelRenderer.SelectedObject.Scale= rot;
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {

        }

        private void numCamPosX_ValueChanged(object sender, EventArgs e)
        {
            Vector2 pos=new Vector2((float)numCamPosX.Value,
                                    (float)numCamPosY.Value);
            SessionData.Instance.CurrentScene.SceneData.Camera.SetPosition( pos);
        }

        private void trackZoom_Scroll(object sender, EventArgs e)
        {


        }
        #endregion

        private void levelRenderer_Click(object sender, EventArgs e)
        {

        }

        private void cboComponents_SelectedIndexChanged(object sender, EventArgs e)
        {

            propertyGrid1.SelectedObject = (CreamXComponent)cboComponents.SelectedItem;
        }
    }
    class ComponentHolder
    {
        public string name;
        public CreamXComponent componentInstance;
    }
}